
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;
using System.IO;
using System.Reflection;
using System.Text;
using System.Xml;

using Swaf.Container;
using Swaf.Instrumentation;

namespace Swaf
{
	/// <summary>
	/// A grab bag of static functions that help get simple yet tedious tasks done.</summary>
	public class Cmn
	{
		/// <summary>
		/// Helper class for incrementing and decrementing performance counters.
		/// </summary>
		public class PerfMon
		{
			protected static IAppInstrumentation s_instrMgr;

			#region CounterUpdateMethods
			/// <summary>
			/// Returns a reference to a performance counter object
			/// </summary>
			/// <param name="counter"></param>
			/// <returns></returns>
			private static IPerformanceCounter getCounter(string counter)
			{
				if (s_instrMgr == null)	
					s_instrMgr=Application.currentApp.appData["sysObjs-AppInstrumentation"] as IAppInstrumentation;
			
				if (s_instrMgr != null)
				{
					return s_instrMgr.getCounter(counter);
				}
				else
					return null;
				
			}
			/// <summary>
			/// Increments a performance counter by 1
			/// </summary>
			/// <param name="counter"></param>
			public static void incrementCounter(string counter)
			{
				try
				{
					if (s_instrMgr == null)
						return;
					IPerformanceCounter thisCounter = getCounter(counter);
					lock(s_instrMgr)
					{
						if (thisCounter != null)
							thisCounter.increment();
					}
				}
				catch(Exception){}
			}
			/// <summary>
			/// Increments a performance counter by specified value
			/// </summary>
			/// <param name="counter"></param>
			/// <param name="incrementValue"></param>
			public static void incrementCounter(string counter, long incrementValue)
			{
				try
				{
					if (s_instrMgr == null)
						return;
					IPerformanceCounter thisCounter = getCounter(counter);
					lock(s_instrMgr)
					{
						if (thisCounter != null)
							thisCounter.incrementBy(incrementValue);
					}
				}

				catch(Exception){}		
			}
			/// <summary>
			/// Decrements a performance counter by 1
			/// </summary>
			/// <param name="counter"></param>
			public static void decrementCounter(string counter)
			{
				try
				{
					if (s_instrMgr == null)
						return;
					IPerformanceCounter thisCounter = getCounter(counter);
					lock(s_instrMgr)
					{
						if (thisCounter != null)
							thisCounter.decrement();
					}
				}
				catch(Exception){}	
	
			}
			/// <summary>
			/// Decrements a performance counter by a specified value
			/// </summary>
			/// <param name="counter"></param>
			/// <param name="decrementValue"></param>
			public static void decrementCounter(string counter, long decrementValue)
			{
				try
				{
					if (s_instrMgr == null)
						return;
					IPerformanceCounter thisCounter = getCounter(counter);
					lock(s_instrMgr)
					{
						if (thisCounter != null)
							thisCounter.decrementBy(decrementValue);
					}
				}
				catch(Exception){}
			}
			#endregion
		}
		/// <summary>
		/// This method gets the information about the calling method, file and line number.
		/// </summary>
		/// <returns></returns>
		public static string getCaller(int level)
		{
			System.Diagnostics.StackFrame callStack = new System.Diagnostics.StackFrame(level, true);
			System.Reflection.MethodBase method = callStack.GetMethod();
			string callingMethod = method.Name;
			string callingClass = method.ReflectedType.FullName;
			return string.Format("{0}.{1}",callingClass, callingMethod);
		}

		/// <summary>Writes the indicated message to the log file.
		/// <p>Sample usage:
		/// <pre>
		/// Application.currentApp.Log.println("blah blah");
		/// </pre>
		/// </p>
		/// </summary>
		public static void  oldWriteLog(string message)
		{
			oldWriteLog(message, true);
		}

		public static void oldWriteLog(string message, bool newLine)
		{
			if(logFile_ != null)
			{
				if (newLine)
				{
					string s = timestamp() + ": ";
					if (s.Equals(writeLogTimestamp_))
						s = ">>> ";
					else
						writeLogTimestamp_ = s;
					logFile_.WriteLine(s + message);
				}
				else
				{
					logFile_.Write(message);
				}
			}
		}

		public static string pathCombine(params string[] parts)
		{
			int length = 0;
			for (int pos = 0; pos < parts.Length; pos++)
			{
				if (parts[pos] == null)
					parts[pos] = "";
				length += parts[pos].Length + 1;
			}
			StringBuilder buff = new StringBuilder(length);
			for(int pos = 0; pos < parts.Length; pos++)
			{
				if (parts[pos] == null)
					parts[pos] = "";
				buff.Append(parts[pos]);
				if (pos < parts.Length -1 && 
					(parts[pos].Length == 0 || parts[pos][parts[pos].Length - 1] != Path.DirectorySeparatorChar) &&
					(parts[pos+1].Length == 0 || parts[pos+1][0] != Path.DirectorySeparatorChar))
					buff.Append(Path.DirectorySeparatorChar);
			}
			if(Path.DirectorySeparatorChar != '/')
				buff.Replace('/', '\\');

			return buff.ToString();
		}

		public static bool IsEmpty(object v)
		{
			if (!(v is string))
				return v == null;
			return IsEmpty((string)v);
		}
		public static bool IsEmpty(string v)
		{
			return string.IsNullOrEmpty(v);
		}

		public static bool IsTrue(object v)
		{
			if (v is bool)
				return (bool)v;
			bool bv = false;
			if(v is string)
				bool.TryParse((string)v, out bv);
			return bv;
		}

		public static bool IsFalse(object v)
		{
			if (v is bool)
				return !(bool)v;
			bool bv = false;
			if (v is string)
				bool.TryParse((string)v, out bv);
			return !bv;
		}

		public static void getAttributes(XmlElement el, string[,] attrs, bool throwExceptionOnAttrNotFound)
		{
			for(int p = 0; p < attrs.GetLength(0); ++p)
			{
				XmlAttribute a = el.GetAttributeNode(attrs[p,0]);
				if(a != null)
					attrs[p,1] = a.Value;
				else if(throwExceptionOnAttrNotFound)
					throw new Exception("The needed attribute '" + attrs[p,0] + "' was not specified");
			}
		}

		/// <summary>The timestamp as it appeared the last time <c>Application.currentApp.Log.println()</c> was called.
		/// If <c>Application.currentApp.Log.println()</c> has not yet been called, this will be <c>null</c>.
		/// </summary>
		private static string writeLogTimestamp_ = null;
		
		/// <summary>Returns the PrintStream that is being used as the log file.</summary>
		/// <remarks>
		/// <p>If all you want to do is write something to this file, you're usually
		/// better off simply using <c>writeLog()</c> method (since that
		/// automatically handles timestamps and other things).</p>
		/// <p>Sample usage:
		/// <pre>
		/// Cmn.logFile().WriteLine("blah blah");  // not recommended
		/// Application.currentApp.Log.println("blah blah");  // recommended
		/// </pre>
		/// </p>
		/// <p>Here's a reasonable way to use this method:
		/// <pre>
		/// try {
		/// // ...
		/// }
		/// catch (Exception e) {
		/// Application.currentApp.Log.println("Unexpected exception: " + e);
		/// e.printStackTrace(Cmn.logFile());
		/// }
		/// </pre></p>
		/// </remarks>
		public static TextWriter logFile
		{
			get{return logFile_;}
			set{logFile_ = value;}
		}
		
		/// <summary>Returns a short string representing the current date and time.
		/// </summary>
		public static string timestamp()
		{
			DateTime n = DateTime.Now;
			return n.ToShortDateString() + " " + n.ToShortTimeString();
		}
		
		/// <summary>This is the exception that is thrown when something goes wrong within
		/// <c>Cmn.callMethod()</c> and/or <c>Cmn.callCtor()</c>.</summary>
		/// <remarks>
		/// The idea is to "contain" the real <c>Exception</c> that was thrown during
		/// those routines, then provide a way for the caller to extract it again.
		/// This simplifies the caller's code -- it needs only one <c>catch</c> handler.
		/// </remarks>
		[Serializable]
		public class ReflectionException : BaseException
		{			
			protected internal Exception m_inner;
			protected internal string m_className;
			protected internal string m_signature;

			public ReflectionException(string className, string signature)
				:base(className + ": " + signature)
			{
				m_inner =  null;
				m_className = className;
				m_signature = signature;
			}

			public ReflectionException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context)
			{
			}

			public ReflectionException(string className, string signature, Exception innerException)
				:base(className + ": " + signature, innerException)
			{
				m_className = className;
				m_signature = signature;
			}

			public ReflectionException(Exception except, string className, string signature)
				: base(signature, except)
			{
				m_inner = except;
				m_className = className;
				m_signature = signature;
			}
			/*
						public override string Message
						{
							get
							{
								Exception inner = m_inner;
								if (inner is System.Reflection.TargetInvocationException)
									inner = (Exception) ((System.Reflection.TargetInvocationException) inner).GetBaseException();
								return inner.Message;
							}
				
						}

			*/			
			/// <summary>Returns the exception that was actually thrown by the <c>System.Reflection</c> primitives.
			/// </summary>
			public virtual Exception exception()
			{
				return this.InnerException;
			}
			
			/// <summary>Returns the name of the class that contains the method or constructor that was called.
			/// For example, if calling a method on a <c>Integer</c> object, this would return <c>System.Int32</c>.
			/// </summary>
			public virtual string className()
			{
				return m_className;
			}
			
			/// <summary>Returns the signature of the method or constructor that was called.
			/// For example, if calling the <c>Parse(String)</c> (static) method
			/// associated with class <c>Int32</c>, this would return something
			/// like <c>System.Int32.Parse(System.String)</c>.</summary>
			/// <remarks>
			/// Note that the specifics of the returned string are not guaranteed,
			/// since if an exception occurred early enough in the process, the
			/// signature may be built by hand.
			/// </remarks>
			public virtual string signature()
			{
				return m_signature;
			}
			

			/*
			public override string ToString()
			{
				Exception inner = m_inner;
				if (inner is System.Reflection.TargetInvocationException)
					inner = (Exception) ((System.Reflection.TargetInvocationException) inner).GetBaseException();
				return inner.ToString();
			}
			*/
		}
		
		/// <summary>Calls the method named <c>name</c> on the object <c>onThis</c>.
		/// Passes the array of <c>paramValues</c> (which are of the classes
		/// found in the array <c>paramTypes</c>) to the method.
		/// <p>This can be used to call both non-static methods (in which case
		/// the <c>onThis</c> parameter will become the actual <c>this</c>
		/// object within the method) or static methods (in which case <c>onThis</c>
		/// is merely used to decide which class to use).</p>
		/// </summary>
		/// <returns>the method's return value.
		/// 
		/// </returns>
		public static object callMethod(object onThis, string name, System.Type[] paramTypes, object[] paramValues)
		{
			object ans = null;
			System.Type clas = null;
			System.Reflection.MethodInfo member = null;
			bool calledMember = false;
			try
			{
				clas = onThis.GetType();
				try
				{
					if(clas.Name.Equals("__ComObject"))
					{
						ans = clas.InvokeMember(name,BindingFlags.InvokeMethod, null, onThis, paramValues);
						return ans;
					}
					member = clas.GetMethod(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null,CallingConventions.Any, (System.Type[]) paramTypes, null);
				}
				catch (Exception e)
				{
					System.Reflection.MethodInfo[] methods = clas.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
					for (int pos = 0; pos < methods.Length; ++pos)
					{
						System.Reflection.MethodInfo m = methods[pos];

						if (m.Name.CompareTo(name) == 0 && m.GetParameters().Length == paramValues.Length)
							try
							{
								ans = m.Invoke(onThis, (object[]) paramValues);
								calledMember = true;
							}
							catch (Exception)
							{
								//Try the next match
							}
					}
				}
				if (calledMember == false && member != null)
					ans = member.Invoke(onThis, (object[]) paramValues);
			}
			catch (Exception e)
			{
				string className = (clas == null)?"(unknown class)":clas.FullName;
				string signature = (member == null)?className + "." + name + "(" + Cmn.join(", ", paramTypes) + ")":member.ToString();
				throw new ReflectionException(e, className, signature);
			}
			
			return ans;
		}
		public static IDictionary getPropertyList(XmlNodeList def, bool ignorePropertyExceptions)
		{
			IProperty prop;
			//IList list = new ArrayList();
			IDictionary map = new Hashtable();

			foreach(XmlNode node in def)
			{
                if (node is XmlElement)
                {
                    try
                    {
                        prop = new BaseProperty((XmlElement)node, ignorePropertyExceptions);
                        //list.Add(prop);
                        map[prop.name] = prop;
                    }
                    catch (Exception) { }
                }
			}
			//return list;
			return map;
		}
		/// <summary>
		/// Static helper method to set a number of properties on a provided object in a single call.
		/// </summary>
		/// <param name="onThis">The object on which to call the specified property set</param>
		/// <param name="names">String array of names of properties to set</param>
		/// <param name="vals">Object array of property values.  One-to-one with names</param>
		public static void setProperties(object onThis, string[] names, object[] vals)
		{
			int numProps = names.Length;
			for (int index = 0; index < numProps;index ++)
			{
				callPropertySet(onThis, names[index], vals[index]);
			}
		}
		/// <summary>
		/// Static helper method to set a number of properties on a provided object in a single call.
		/// </summary>
		/// <param name="onThis">The object on which to call the specified property set</param>
		/// <param name="list">IList containing IProperty objects</param>
		public static void setProperties(object onThis, IList list)
		{
			foreach (IProperty prop in list)
			{
				prop.callSet(onThis);
			}
		}

		public static void setProperties(object onThis, IDictionary list)
		{
			foreach(DictionaryEntry e in list)
				Cmn.callPropertySet(onThis, e.Key.ToString(), e.Value);
		}

		public static IList getProperties(object onThis)
		{
			IList ret = new ArrayList();
			System.Type clas = null;
			PropertyInfo[] propInfos;
			IProperty prop;
			clas = onThis.GetType();
			propInfos = clas.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach(PropertyInfo propInfo in propInfos)
			{
				if (propInfo.CanWrite)
				{
					prop = new BaseProperty(propInfo.Name,Cmn.callPropertyGet(onThis,propInfo.Name),false,true);
					ret.Add(prop);
				}
			}
			return ret;
		}
		public static void callPropertySet(object onThis, string name, object val)
		{
			callPropertySet(onThis, name, val, false);
		}

		public static void callPropertySet(object onThis, string name, object val, bool convertValue)
		{
			System.Type clas = null;
			System.Reflection.PropertyInfo member = null;
			System.Reflection.FieldInfo fieldMember = null;
			try
			{
				if (val is BaseProperty)
				{
					((BaseProperty) val).callSet(onThis);
				}
				else
				{
					clas = onThis.GetType();
					member = clas.GetProperty(name, BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.NonPublic);
					if (member == null)
						fieldMember = clas.GetField(name, BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.NonPublic | BindingFlags.Public);
					if (convertValue && member != null && val != null && member.PropertyType != val.GetType())
					{
						val = Cmn.callCtor(member.PropertyType.FullName, new Type[]{val.GetType()}, new object[]{val});
					}
					try
					{
						if(member != null)
							member.SetValue(onThis, val,null);
						else if(fieldMember != null)
							fieldMember.SetValue(onThis, val);
							
					}
					catch(Exception e)
					{
						string v = val.ToString();
						int pos = v.LastIndexOf(".");
						if(pos > 0)
						{
#if(!COMPACT_FRAMEWORK)
							object realVal = Cmn.callStatic(member.PropertyType.AssemblyQualifiedName, v.Substring(pos+1), new Type[0], new object[0]);
#else
						object realVal = Cmn.callStatic(member.PropertyType.FullName, v.Substring(pos+1), new Type[0], new object[0]);
#endif
							member.SetValue(onThis, realVal,null);
						}
						else
							member.SetValue(onThis, v, null);
					}
				}
			}
			catch(InvalidPropertyException)
			{
				throw;
			}
			catch (Exception e)
			{
				string className = (clas == null)?"(unknown class)":clas.FullName;
				string signature = (member == null)?className + "." + name:member.ToString();
				throw new ReflectionException(e, className, signature);
			}
		}

        public static object callPropertyGet(object onThis, string name, object[] indexerParam)
        {
			return callPropertyGet(onThis, name, indexerParam, true);
        }
		public static object callPropertyGet(object onThis, string name, object[] indexerParam, bool throwException)
		{
			System.Type clas = null;
			System.Reflection.PropertyInfo member = null;
			System.Reflection.FieldInfo fieldMember = null;
			try
            {
                clas = onThis.GetType();
                if (clas.Name.Equals("__ComObject"))
                {
					return clas.InvokeMember(name, BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.NonPublic, null, onThis, indexerParam);
                }
				member = clas.GetProperty(name, BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.NonPublic);
				if (member == null)
					fieldMember = clas.GetField(name, BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.NonPublic | BindingFlags.Public);
				if (member != null)
					return member.GetValue(onThis, indexerParam);
				else if (fieldMember != null)
					return fieldMember.GetValue(onThis);
				else if (throwException)
                    throw new ReflectionException(clas.FullName, name);
            }
            catch (ReflectionException e) {throw;}
            catch (Exception e)
            {
                if (throwException)
                {
                    string className = (clas == null) ? "(unknown class)" : clas.FullName;
                    string signature = (member == null) ? className + "." + name : member.ToString();
                    throw new ReflectionException(e, className, signature);
                }
            }
            return null;
		}
        public static object callPropertyGet(object onThis, string name, bool throwException)
        {
            return callPropertyGet(onThis, name, null,  throwException);
        }
		public static object callPropertyGet(object onThis, string name)
		{
			return callPropertyGet(onThis, name, null);
		}

		public static void addEventObserver(object onThis, string eventName, object observer, string observerMethod)
		{
			System.Type clas = null;
			System.Reflection.EventInfo member = null;

			try
			{
				clas = onThis.GetType();
				member = clas.GetEvent(eventName);

				System.Type clas2 = observer.GetType();
				System.Reflection.MethodInfo member2 = clas2.GetMethod(observerMethod);
				System.Reflection.ConstructorInfo[] c = member.EventHandlerType.GetConstructors();
				
				if(c != null)
				{
					member.AddEventHandler(onThis, (System.Delegate)c[0].Invoke(new object[]{observer, member2.MethodHandle.GetFunctionPointer()}));
				}
			}
			catch (Exception e)
			{
				string className = (clas == null)?"(unknown class)":clas.FullName;
				string signature = (member == null)?className + "." + eventName:member.ToString();
				throw new ReflectionException(e, className, signature);
			}
		}
		
		private static FlexiMap s_classTypes = new FlexiMap();

		public static object findAndCallCtor(string classname, Type[][] paramTypes, object[][] paramValues)
		{
			Type classType = findType(classname);
			if (classType != null)
			{
				for (int x = 0; x < paramTypes.Length; ++x)
				{
					object ans = createType(classType, paramTypes[x], paramValues[x], false);
					if (ans != null)
						return ans;
				}
			}
			throw new ReflectionException(classname, 
				String.Format("Able to load assembly, but cannot find class with name {0}", classname));
		}

		public static object callCtor(string classname)
		{
			return callCtor(classname, new Type[0], new object[0]);
		}

		/// <summary>Calls the constructor of the class named <c>classname</c>.
		/// Passes the array of <c>paramValues</c> (which are of the classes
		/// found in the array <c>paramTypes</c>) to the method.
		/// </summary>
		/// <returns>the constructed object.
		/// 
		/// </returns>
		public static object callCtor(string classname, Type[] paramTypes, object[] paramValues)
		{
			object ans = null;

			Type classType = findType(classname);
			if (classType != null)
				ans = createType(classType, paramTypes, paramValues, true);
			else
				throw new ReflectionException(classname, "Able to load assembly, but cannot find class with given name");
			return ans;
		}

		public static Assembly findAssembly(string assName)
		{
			Application.currentApp.Log.println(Application.NORMAL_STARTUP_LOG_LEVEL, "Will look for assembly {0}", assName);
			string ending = ".dll";
			if (assName.EndsWith(".exe", StringComparison.CurrentCultureIgnoreCase))
				ending = ".exe";
			Assembly a = null;
			Assembly[] loaded = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly ass in loaded)
			{
				string loadedAssName = ass.FullName;
				loadedAssName = loadedAssName.Substring(0, loadedAssName.IndexOf(',')) + ending;
				if (assName.StartsWith(loadedAssName))
					return ass;
			}
			Application.currentApp.Log.println(Application.NORMAL_STARTUP_LOG_LEVEL, "Couldnt find assembly {0} loaded already", assName);
			/*
			if (!assName.StartsWith("$BaseBinLocation$"))
				assName = "$BaseBinLocation$" + assName;

			assName = Application.currentApp.resMgr.resolve(assName);
			if (assName.StartsWith("file://"))
				assName = assName.Substring("file://".Length);
			 */
			assName = assName.Substring(0, assName.Length - 4);
			Application.currentApp.Log.println(Application.NORMAL_STARTUP_LOG_LEVEL, "Will try to load assembly {0} by load", assName);
			Application.currentApp.Log.println(Application.NORMAL_STARTUP_LOG_LEVEL, "Current directory is {0}", Directory.GetCurrentDirectory());
			try
			{
				a = Assembly.Load(assName);
			}
			catch (Exception e)
			{
				Application.currentApp.Log.println(Application.NORMAL_STARTUP_LOG_LEVEL, "Could not load assembly due to: {0}", e);
			}
			if(a == null)
				try
				{
					a = Assembly.LoadWithPartialName(assName);
					if (a == null)
						a = Assembly.LoadFrom(assName);
				}
				catch (Exception)
				{
				}

			if(a == null)
				Application.currentApp.Log.println(Application.NORMAL_STARTUP_LOG_LEVEL, "Never could load assembly needed: {0}", assName);
			return a;
		}

		public static Type findType(string classname)
		{
			string mapKey = classname.Replace('.', '-');
			Type classType = (Type)s_classTypes.get(mapKey);

			if (classType == null)
			{
				classType = Type.GetType(classname);
				if (classType == null)
				{
					int pos = classname.IndexOf(',');
					if (pos != -1)
					{

						Assembly a = null;
						string assemblyName = Application.currentApp.resMgr.getMacro("RootLoadAssembly");
						string cname = classname.Substring(0, pos);
						if (!Cmn.IsEmpty(assemblyName))
						{
							try
							{
								if (assemblyName.EndsWith(".exe"))
									assemblyName = assemblyName.Substring(0, assemblyName.Length - 4);
								a = Assembly.Load(assemblyName);
								classType = a.GetType(cname);
								if (classType == null)
									a = null;
							}
							catch (Exception)
							{
								a = null;
							}
						}

						if(a == null)
						{

							//TODO: This code assumes that assembly names always are in the format
							//of just the filename minus extension such as .dll.'
							//#if(!COMPACT_FRAMEWORK)
							//						string asmName = classname.Substring(pos +1);
							//						//AssemblyName an = AssemblyName.GetAssemblyName(asmName);
							//						Assembly a = Assembly.LoadWithPartialName(asmName);
							string curDir = Directory.GetCurrentDirectory();
							//#else

							assemblyName = classname.Substring(pos + 1);

							string assemblyExt = assemblyName.Substring(assemblyName.Length - 4);
							if (String.Compare(assemblyExt, ".dll", false) != 0 &&
								String.Compare(assemblyExt, ".exe", false) != 0)
								assemblyName += ".dll";
							try
							{
								string tmpPartialName = classname.Substring(pos + 1);
								a = Assembly.LoadWithPartialName(tmpPartialName);
								if (a == null)
									a = Assembly.LoadFrom(assemblyName);
							}
							catch (Exception e)
							{
								throw new ReflectionException(classname.Substring(0, pos), String.Format("Unable to load assembly '{0}'.", assemblyName), e);
							}
						}
						//#endif
						if (a != null)
							classType = a.GetType(cname);
						else
							throw new ReflectionException(cname, String.Format("Unable to load assembly '{0}'.", assemblyName));
					}
				}
				if(classType != null)
					s_classTypes.put(mapKey, classType);

			}
			return classType;
		}

		public static object createType(Type theType, string theVal)
		{
			return createType(theType, new Type[]{typeof(string)}, new object[]{theVal}, true);
		}

		public static object createType(Type classType, Type[] paramTypes, object[] paramValues, bool throwException)
		{
			MethodBase ctor = null;
			object ans = null;
            if (classType.FullName == "System.String")
                {
                    if (paramValues.Length > 0)
                    {
                        if (paramValues[0] == null)
                            return null; //Out of place to return here, but we need to short-cut things when a string is being created with a null, which means a null should be returned as well.
                        else
                            return paramValues[0].ToString();
                    }
                }
			if (!classType.IsValueType)
				ctor = classType.GetConstructor(paramTypes);
			else 
			{
				if(classType.IsEnum)
				{
					if(paramValues.Length != 0)
						return System.Enum.Parse(classType, paramValues[0].ToString(), true);
					else
						return null; //TODO: Is it really appropriate to return null when trying to create a enum type but didn't specify the enum string to parse?
				}
                else
				{
					//This chunk of code will try to call a static method called Parse
					//if the classType is a value type.  Many .NET value types have
					//a Parse method rather than a constructor.
					ctor = classType.GetMethod("Parse", new Type[]{typeof(String)});
					if(paramValues.Length == 0)
					{
						if(classType == typeof(Int32))
							return 0;
						else if(classType == typeof(Int16))
							return (Int16)0;
						else if(classType == typeof(Int64))
							return (Int64)0;
						else if(classType == typeof(Double))
							return (Double)0.0;
						else if(classType == typeof(bool))
							return false;
						else if(classType == typeof(byte))
							return (byte)0;
						else if(classType == typeof(DateTime))
							return new DateTime();
						else if(classType == typeof(char))
							return (char)0;
						paramValues = new object[]{""};
					}
					else if (paramValues.Length != 1)
					{
						//This is done because calling toString on datetime will clip milliseconds and the
						//newly created date wont match the given date exactly (it would down to the second only).
						//This check avoids that scenario.
						if (classType == typeof(DateTime) && paramValues[0] is DateTime)
							return paramValues[0];

						paramValues = new object[] { paramValues[0].ToString() };
					}
				}
			}
			if(ctor == null)
			{
				//Because Class.getConstructor only matches exact types and not "kind ofs", we
				//will look for a matching constructor with looser requirements
				System.Reflection.ConstructorInfo[] ctors = classType.GetConstructors();
				for (int pos = 0; pos < ctors.Length; ++pos)
				{
					try
					{
						if(ctors[pos].GetParameters().Length == paramValues.Length)
							return ctors[pos].Invoke(paramValues);
					}
					catch (Exception)
					{

						//Intentionally left blank, try the next contrstructor...
					}
				}
				//We didn't successfully call a constructor so rethrow the error.
				if(throwException)
					throw new ReflectionException(classType.Name, 
						String.Format("Able to load assembly, and find class type, but cannot find constructor or 'Parse' method that takes the param types '{0}'", paramTypes));
			}
			else
			{
				try
				{
					if(ctor.IsConstructor)
						ans = ((ConstructorInfo)ctor).Invoke(paramValues);
					else if(ctor.IsStatic) //try and call as a static method to create
						ans = ctor.Invoke(null,paramValues);
				}
				catch(Exception e)
				{
					//The above code attempted to create an instance of the object given a null param value, which failed.
					//This code will return null based on that situation.
					if(paramValues != null && paramValues.Length > 0 && paramValues[0] == null)
						return null;
					if (e is TargetInvocationException && e.InnerException != null)
						e = e.InnerException;
					if(throwException)
						throw new ReflectionException(e, classType.Name, "Unable to create instance of given class type because an exception was thrown during construction");
				}
			}
			
			return ans;
		}

		public static object callStatic(string classname, string methodname, Type[] paramTypes, object[] paramValues)
		{
			Type ctype = findType(classname);
			//Type ctype = Type.GetType(classname);
			if(ctype != null)
			{
				MemberInfo[] m = ctype.GetMember(methodname, BindingFlags.Static | BindingFlags.Public);
				if(m.Length > 0)
				{
					if(m[0].MemberType == MemberTypes.Method)
						return ctype.InvokeMember(methodname, BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static,null, null, paramValues);
						//return ((MethodInfo)m[0]).Invoke(null, paramValues);
					else if(m[0].MemberType == MemberTypes.Property)
						return ((PropertyInfo)m[0]).GetValue(null, new object[0]);
					else if(m[0].MemberType == MemberTypes.Field)
						return ((FieldInfo)m[0]).GetValue(null);
				}
				else
					throw new ReflectionException(classname, "Able to load assembly, but cannot find static method with given name");
			}
			throw new ReflectionException(classname, "Unable to load assembly for static method call");
		}
		
		/// <summary>Returns true if-and-only-if 'classname' exists as a valid .NET class.
		/// </summary>
		/// <param name="classname">the fully qualified name of the class.  e.g., "System.String" (<em>not</em> "String").
		/// 
		/// </param>
		public static bool classExists(string classname)
		{
			try
			{
				Type ctype = findType(classname);
				return ctype != null;
			}
			catch (Exception)
			{
			}
			return false;
		}
		
		/// <summary>This joins the array of elements 'a' using the separator 's' between each one.
		/// It's almost the opposite of <c>PatternUsage.split(...)</c>.</summary>
		public static string join(string s, object[] a)
		{
			return join(s, a.GetEnumerator());
		}
		
		/// <summary>This joins the elements of Collection 'c' using the separator 's' between each one.
		/// It's almost the opposite of <c>PatternUsage.split(...)</c>.</summary>
		public static string join(string s, IEnumerator i)
		{
			System.Text.StringBuilder ans = new System.Text.StringBuilder();
			while(i.MoveNext())
				ans.Append(i.Current);
			return ans.ToString(0, ans.Length);
		}
		
		/// <summary>If <c>target</c> appears within <c>s</c>, replaces the first such occurance with <c>replacement</c>.
		/// If <c>target</c> does not appear within <c>s</c>, returns <c>s</c> unchanged.
		/// </summary>
		public static string replaceOneLiteral(string s, string target, string replacement)
		{
			int i = s.IndexOf(target);
			return (i == - 1)?(s):(s.Substring(0, (i) - (0)) + replacement + s.Substring(i + target.Length));
		}
		
		private class ReplaceOccurance : IComparable
		{
			public ReplaceOccurance(int position, int paramLen, int replaceLen, string replaceText)
			{
				this.position = position;
				this.paramLen = paramLen;
				this.replaceLen = replaceLen;
				this.replaceText = replaceText;
			}
			public int position;
			public int paramLen;
			public int replaceLen;
			public string replaceText;
			#region IComparable Members

			public int CompareTo(object obj)
			{
				if(((ReplaceOccurance)obj).position == position)
					return 0;
				return (position < ((ReplaceOccurance)obj).position)?-1:1;
			}

			#endregion
		}

		protected static string replace(IDictionary replacements, string source, out bool changed)
		{
			ArrayList occurances = new ArrayList();
			int capacity = source.Length;
			int pos = 0;
			string val;
			foreach(string name in replacements.Keys)
			{
				pos = 0;
				while((pos = source.IndexOf(name, pos)) != -1)
				{
					val = (string)replacements[name];
					capacity += (val.Length - name.Length);
					occurances.Add(new ReplaceOccurance(pos, name.Length, val.Length, val));
					if(++pos >= source.Length) break;
					
				}
			}

			if(occurances.Count != 0)
			{
				occurances.Sort();
				StringBuilder b = new StringBuilder(capacity);
				pos = 0;
				int len = 0;
				foreach(ReplaceOccurance o in occurances)
				{
					len = o.position - pos;
					b.Append(source.Substring(pos, len));
					b.Append(o.replaceText);
					pos += o.paramLen + len;
				}
				b.Append(source.Substring(pos));
				changed=true;
				return b.ToString();
			}
			changed=false;
			return source;
		}
		public static string replaceAll(IDictionary replacements, string source)
		{
			string newSource=source;
			bool changed=true;
			while (changed)
			{
				newSource=replace(replacements,newSource,out changed);
			}
			return newSource;
		}
		public static string replace(IDictionary replacements, string source)
		{
			bool changed=false;
			return replace(replacements,source,out changed);
		}
		
		/// <summary>Call this when there is an unexpected internal error and you wish to get
		/// some debugging information about the specifics.  During development, this
		/// kills the application and puts out a stack trace showing where you were.
		/// During production we may change the implementation of this so it's not so
		/// drastic.
		/// </summary>
		public static void  internalError(string message)
		{
			// TO-DO: consider changing this implementation when we go live
			throw new System.ApplicationException(message);
		}
		
		/// <summary>Returns a String that contains 'count' copies of the String 's'.
		/// </summary>
		/// <param name="s">the string to be duplicated.
		/// </param>
		/// <param name="count">the number of copies of 's' to include in the result.
		/// 
		/// </param>
		public static string repeat(string s, int count)
		{
			System.Text.StringBuilder b = new System.Text.StringBuilder(s.Length * count);
			while (count-- > 0)
			{
				b.Append(s);
			}
			return b.ToString();
		}
		
		
		
		/// <summary>Tokenizes the String using the first character as a delimiter.
		/// </summary>
		/// <param name="s">the String to be tokenized.
		/// Must not be <c>null</c>.
		/// Must not be the empty string, <c>""</c>.
		/// </param>
		/// <returns>the IList of tokens (not including the empty token before the first character).
		/// E.g., if 's' is "|abc|def|pqr", this will return a list containg ["abc", "def", "pqr"].
		/// E.g., if 's' is "|||" this will return a list containing ["", "", ""].
		/// 
		/// </returns>
		public static IList tokenizeOnFirstChar(string s)
		{
			if (s == null)
				s = "";
			IList ans = (IList) tokenizeOnFirstChar_[s];
			if (ans == null)
			{
				if(s.Length == 0)
					ans = new string[0];
				else
				{
					ans = s.Substring(1).Split(s[0]);
					tokenizeOnFirstChar_[s] = ans;
				}
			}
			return ans;
		}
		
		/// <summary>Allocates a new array that is <c>newLength</c> elements, and copies whatever fits from <c>origArray</c>.
		/// If we're creating a shorter copy (if <c>newLength &lt; origArray.length</c>),
		/// copies only <c>newLength</c> elements.
		/// If we're creating a longer copy (if <c>newLength &gt; origArray.length</c>),
		/// copies only <c>origArray.length</c> elements.
		/// </summary>
		/// <param name="origArray">the original array.
		/// Must be an array.
		/// </param>
		/// <param name="newLength">the desired length of the resulting array.
		/// Must not be negative.
		/// </param>
		/// <returns>the resulting array.
		/// Returned array's length will be <c>newLength</c>.
		/// 
		/// </returns>
		public static object copyArray(object origArray, int newLength)
		{
			int origLength = ((Array) origArray).Length;
			System.Type componentType = origArray.GetType().GetElementType();
			object destArray = System.Array.CreateInstance(componentType, newLength);
			Array.Copy((Array)origArray, 0, (Array)destArray, 0, System.Math.Min(origLength, newLength));
			return destArray;
		}
		
		/// <summary>Allocates a new array that is one element longer than <c>origArray</c>,
		/// and adds the specified element to the end.
		/// </summary>
		/// <param name="origArray">the original array.
		/// Must be an array.
		/// </param>
		/// <param name="elem">the element to be added to the newly allocated array.
		/// Must be type-compatible with the type of the array
		/// (e.g., if <c>origArray</c> is an array of <c>Apple</c>,
		/// <c>elem</c> must be assignment compatible to an <c>Apple</c>).
		/// </param>
		/// <returns>the resulting array.
		/// Returned array's length will be <c>origArray.length + 1</c>.
		/// The first <c>origArray.length</c> elements will be <c>==</c> to those in <c>origArray</c>.
		/// The last element will be <c>==</c> to <c>elem</c>.
		/// </returns>
		public static object appendToArray(Array origArray, object elem)
		{
			int origLength = origArray.Length;
			System.Type componentType = origArray.GetType().GetElementType();
			Array destArray = System.Array.CreateInstance(componentType, origLength + 1);
			Array.Copy(origArray, 0, destArray, 0, origLength);
			((Array)destArray).SetValue(elem, origLength);
			return destArray;
		}

		public static XmlElement findChildWithName(XmlElement parent, string name)
		{
			for(XmlNode el = parent.FirstChild; el != null; el = el.NextSibling)
				if(el.Name.Equals(name))
					return (XmlElement)el;
			return null;
		}

		static Cmn()
		{
			tokenizeOnFirstChar_ = new Hashtable();
#if(!COMPACT_FRAMEWORK)
			logFile_ = System.Console.Out;
#endif
		}
		
		/// <summary>
		/// This is a cache used exclusively by <c>tokenizeOnFirstChar()</c>.</summary>
		protected internal static IDictionary tokenizeOnFirstChar_;
		
		/// <summary>
		/// The PrintStream that is being used as the log file.</summary>
		protected internal static System.IO.TextWriter logFile_;
	}

	public enum DetailLevel { idOnly, idAndTitle, idAndTitlePlus, basicDetails, fullDetails }
}